#include "hinetconfig.h"
using namespace std;

bool get_local_mac(char * mac, const char * eth_name)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock == -1)
    {
        hi_error("%s: Get Local MAC Fail!(socket)\r\n", eth_name);
        return false;
    }

    ifreq ifr;
    strncpy(ifr.ifr_name, eth_name, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    if(ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
    {
        hi_error("%s: Get Local MAC Fail!(ioctl)\r\n", eth_name);
        close(sock);
        return false;
    }

    //   memcpy(mac, ifr.ifr_addr.sa_data, 6);
    //	printf("mac:%.2X:%.2X:%.2X:%.2X:%0.2X:%0.2X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
             (unsigned char)ifr.ifr_hwaddr.sa_data[0],
            (unsigned char)ifr.ifr_hwaddr.sa_data[1],
            (unsigned char)ifr.ifr_hwaddr.sa_data[2],
            (unsigned char)ifr.ifr_hwaddr.sa_data[3],
            (unsigned char)ifr.ifr_hwaddr.sa_data[4],
            (unsigned char)ifr.ifr_hwaddr.sa_data[5]);

    close(sock);
    return true;
}

bool get_local_netmask(char* netmask, const char * eth_name)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock == -1)
    {
        hi_error("Error: Get Local NETMASK Fail!(socket)\r\n");
        return false;
    }

    ifreq ifr;
    strncpy(ifr.ifr_name, eth_name, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    if(ioctl(sock, SIOCGIFNETMASK, &ifr) < 0)
    {
        hi_error("Error: Get Local NETMASK Fail!(ioctl)\r\n");
        close(sock);
        return false;
    }

    strcpy(netmask, inet_ntoa(((sockaddr_in *)&ifr.ifr_netmask)->sin_addr));
    //	printf("netmask:0x%x\r\n", netmask);

    close(sock);
    return true;
}

bool get_local_broadaddr(unsigned long & broadaddr, char * eth_name)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock == -1)
    {
        hi_error("Error: Get Local BROADADDR Fail!(socket)\r\n");
        return false;
    }

    ifreq ifr;
    strncpy(ifr.ifr_name, eth_name, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    if(ioctl(sock, SIOCGIFBRDADDR, &ifr) < 0)
    {
        hi_error("Error: Get Local BROADADDR Fail!(ioctl)\r\n");
        close(sock);
        return false;
    }

    memcpy(&broadaddr, &(((sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr), sizeof(broadaddr));
    //	printf("broadaddr:0x%x\r\n", broadaddr);

    close(sock);
    return true;
}

bool get_local_ip(char* ip, const char * eth_name)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock == -1)
    {
        hi_error("Error: Get Local IP Fail!(socket)\r\n");
        return false;
    }

    ifreq ifr;
    strncpy(ifr.ifr_name, eth_name, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    if(ioctl(sock, SIOCGIFADDR, &ifr) < 0)
    {
        hi_error("Error: Get Local IP Fail!(ioctl)\r\n");
        close(sock);
        return false;
    }

    strcpy(ip, inet_ntoa(((sockaddr_in *)&ifr.ifr_addr)->sin_addr));
    //hi_trace("%s\r\n",ip);

    close(sock);
    return true;
}

string mac2string(char* mac)
{
    char buffer[64];
    sprintf(buffer, "%.2X:%.2X:%.2X:%.2X:%0.2X:%0.2X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return buffer;
}

bool get_local_dns(char *dns)
{
    FILE *fp;
    char buffer[256];
    char buf[20];
    memset(buffer,'\0',sizeof(buffer));
    memset(buf,'\0',sizeof(buf));
    char *p = NULL;
    fp = fopen("/etc/resolv.conf", "r");
    if (fp == NULL)
        return -1;

    fgets(buffer, sizeof(buffer), fp);

    int i = 0;
    int len = 0;
    p = buffer;
    while(*p != '\0' && *p != '\n')
    {
        if( *p >='0' && *p <='9' || *p == '.')
        {
            buf[i++] = *p;
            len++;
        }
        p++;

        if(len >= 17)
        {
            break;
        }
    }

    if( buf[0] >='0' && buf[0] <='9')
    {
        strcpy(dns,buf);
    }

    return true;
}



bool get_local_gw(char * gateway)
{
    unsigned long p;
    FILE *fp;
    char buf[256]; // 128 is enough for linux
    char iface[16];
    unsigned long dest_addr, gate_addr;
    p = INADDR_NONE;
    fp = fopen("/proc/net/route", "r");
    if (fp == NULL)
        return -1;
    /* Skip title line */
    fgets(buf, sizeof(buf), fp);
    while (fgets(buf, sizeof(buf), fp)) {
        if (sscanf(buf, "%s\t%lX\t%lX", iface, &dest_addr, &gate_addr) != 3 ||
                dest_addr != 0)
            continue;
        p = gate_addr;
        break;
    }

    struct in_addr __in;
    __in.s_addr=p ;

    strcpy(gateway,inet_ntoa(__in));
    fclose(fp);
    return true;
}


int set_local_gw(unsigned long gw)
{
    int skfd;
    struct rtentry rt;
    int err;

    skfd = socket(PF_INET, SOCK_DGRAM, 0);
    if (skfd < 0)
        return -1;

    /* Delete existing defalt gateway */
    memset(&rt, 0, sizeof(rt));

    rt.rt_dst.sa_family = AF_INET;
    ((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0;

    rt.rt_genmask.sa_family = AF_INET;
    ((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0;

    rt.rt_flags = RTF_UP;

    err = ioctl(skfd, SIOCDELRT, &rt);

    if ((err == 0 || errno == ESRCH) && gw) {
        /* Set default gateway */
        memset(&rt, 0, sizeof(rt));

        rt.rt_dst.sa_family = AF_INET;
        ((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0;

        rt.rt_gateway.sa_family = AF_INET;
        ((struct sockaddr_in *)&rt.rt_gateway)->sin_addr.s_addr = gw;

        rt.rt_genmask.sa_family = AF_INET;
        ((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0;

        rt.rt_flags = RTF_UP | RTF_GATEWAY;

        err = ioctl(skfd, SIOCADDRT, &rt);
    }

    close(skfd);

    return err;

}



HiNetConfig::HiNetConfig()
{

}

void HiNetConfig::change_ip(const char *ip, const char *mask, const char *eth_name)
{
    char buf[512] = {0};    
    sprintf(buf, "ifconfig %s %s netmask %s", eth_name, ip, mask);
    hi_trace("shell cmd: %s", buf);
    system(buf);
    return;
}

void HiNetConfig::change_gw(const char *gate, const char *eth_name)
{
    if(gate == NULL) return;
    if(eth_name == NULL) return;
    char buf[100] = {0};
    sprintf(buf, "route del default dev %s", eth_name);
    hi_trace("shell cmd: %s", buf);
    system(buf);

    sprintf(buf, "route add default gw %s dev %s", gate, eth_name);
    hi_trace("shell cmd: %s", buf);
    system(buf);
}

void HiNetConfig::change_mac(const char *mac, const char *eth_name)
{
    if(0 == strcmp(mac, "00:00:00:00:00:00"))
        return;

    char old_mac[32] = {0};
    bool b = get_local_mac(old_mac, eth_name);
    if(b)
    {
        string s = mac2string(old_mac);
        if(0 == strcasecmp(mac, s.c_str()))return;
        //printf("%s:%s->%s\n", eth_name, s.c_str(), mac);
    }

    char buffer[1024];
    sprintf(buffer, "ifconfig %s down", eth_name);
    system(buffer);

    sprintf(buffer, "ifconfig %s hw ether %s", eth_name, mac);
    hi_trace("%s", buffer);
    system(buffer);

    sprintf(buffer, "ifconfig %s up", eth_name);
    system(buffer);

    usleep(1000 * 10);
}

void HiNetConfig::change_dns(const char *dns)
{
    char buffer[512];
    sprintf(buffer, "echo 'nameserver %s' > /etc/resolv.conf", dns);
    system(buffer);
    usleep(1000 * 10);
}

bool get_local_ethernet(std::list<struct net_card> &ethernet)
{
    register int fd, intrface;
    struct ifreq buf[16];
    struct ifconf ifc;
    if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0)
    {
        ifc.ifc_len = sizeof buf;
        ifc.ifc_buf = (caddr_t) buf;
        if (!ioctl (fd, SIOCGIFCONF, (char *) &ifc))
        {
            //获取接口信息
            intrface = ifc.ifc_len / sizeof (struct ifreq);
            while (intrface-- > 0)
            {
                if (!(ioctl (fd, SIOCGIFFLAGS, (char *) &buf[intrface])))
                {

                    struct net_card ethernet_info;
                    ethernet_info.eth_name.clear();

                    ethernet_info.eth_name.append(buf[intrface].ifr_ifrn.ifrn_name);

                    if(buf[intrface].ifr_flags & IFF_UP)
                    {
                        ethernet_info.is_enabled = 1;
                    }
                    else
                    {
                        ethernet_info.is_enabled = 0;
                    }

                    if(buf[intrface].ifr_flags &IFF_LOOPBACK)
                    {
                        ethernet.push_back(ethernet_info);
                    }
                    else if(ethernet_info.is_enabled == 1)
                    {
                        ethernet.push_front(ethernet_info);
                    }
                    else
                    {
                        ethernet.push_back(ethernet_info);
                    }

                }

            }
        }
    }

    return true;
}

bool get_local_ethernet_linkstate(const char *eth_name, int *is_link)
{
    struct ethtool_value {
        __uint32_t      cmd;
        __uint32_t      data;
    };

    struct ethtool_value edata;
    int fd = -1, err = 0;
    struct ifreq ifr;

    memset(&ifr, 0, sizeof(ifr));
    strcpy(ifr.ifr_name, eth_name);
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        perror("Cannot get control socket");
        return false;
    }

    edata.cmd = 0x0000000a;
    ifr.ifr_data = (caddr_t)&edata;
    err = ioctl(fd, 0x8946, &ifr);
    if (err == 0)
    {
        *is_link = edata.data;

    } else if (errno != EOPNOTSUPP) {
        perror("Cannot get link status");
    }

    close(fd);
    return true;
}
